package com.yang.service.Impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.yang.entity.OrderDetail;
import com.yang.entity.Orders;
import com.yang.entity.commodityDetail.Skus;
import com.yang.interceptors.LogExecutionTime;
import com.yang.mapper.OrderMapper;
import com.yang.service.OrderService;
import com.yang.utils.ThreadLocalUtil;
import jakarta.annotation.Resource;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    @Transactional
    public long createOrder(List<OrderDetail> orderDetails) {
        // 获取用户id
        Map<String, Object> map = ThreadLocalUtil.get();
        // TODO 这里的userId换成long类型后
        //  报错java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.Long (java.lang.Integer and java.lang.Long are in module java.base of loader 'bootstrap')
        //  找不到是哪里的问题，搁置
        Integer userId = (Integer) map.get("userId");

        // 如果订单未支付，则不允许创建订单，TODO 可以在创建订单时存入redis中，然后在redis中查询是否存在未支付的订单
        if (orderMapper.getOrderStatus(Long.valueOf(userId)) >= 1) {
            return 1;
        }

        // 数据集
        Orders order = new Orders();
        order.setUserId(Long.valueOf(userId));
        // 计算总金额
        BigDecimal totalAmount = orderDetails.stream()
                .map(OrderDetail::getTotalPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        order.setTotalAmount(totalAmount);
        order.setPaymentMethod("支付宝");
        // 获取系统当前时间并格式化
        String times = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        order.setCreateTime(times);

        // 保存订单
        orderMapper.insertOrder(order);

        // 获取刚刚存入订单的订单id
        // TODO 这里的id应该换种方式获取，如果通过查询创建的订单id，可能会出现查询错误
        long orderId = orderMapper.getInsertId(Long.valueOf(userId));
        // 保存订单详情
        for (OrderDetail orderDetail : orderDetails) {
            orderDetail.setOrdersId(orderId);
            orderDetail.setUserId(Long.valueOf(userId));
            // TODO 这里的地址id应该由前端传过来
            orderDetail.setAddressId(orderDetails.getFirst().getAddressId());
            orderDetail.setReceiverName(orderDetails.getLast().getReceiverName());
            orderDetail.setReceiverPhone(orderDetails.getLast().getReceiverPhone());
            orderDetail.setReceiverAddressDescription(orderDetails.getLast().getReceiverAddressDescription());
            orderDetail.setOrderStatus(1);
            orderDetail.setCreateTime(times);
        }
        orderMapper.insertOrderDetailList(orderDetails);
        // 更新redis中的订单数据
        updateRedisOrders(userId);
        return orderId;
    }

    @Override
    @Transactional
    public void updateOrderStatus(int i, long orderId, long userId) {
        String times = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        // 更新mysql数据
        orderMapper.updateOrderStatus(orderId, i, times);
        // 更新redis中的订单数据
        updateRedisOrders(userId);
    }

    @Override
    public int updateAddressOfOrders(List<Integer> orderDetailId, Integer addressId) {
        // 获取用户id TODO 这里也可以不使用用户id 因为订单id是唯一的
        Map<String, Object> map = ThreadLocalUtil.get();
        // TODO 这里的userId后续可以换成long类型
        int userId = (int) map.get("userId");
        // 获取系统当前时间并格式化
        String times = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        int i = orderMapper.updateAddressOfOrders(orderDetailId, addressId, times);
        updateRedisOrders(userId);
        return i;
    }

    @Override
    public List<OrderDetail> getOrders(int pageNum) {
        Map<String, Object> map = ThreadLocalUtil.get();
        // TODO 这里的userId后续可以换成long类型
        int userId = (int) map.get("userId");
        // key
        String key = "user_" + userId;
        // 结果集
        List<OrderDetail> orderDetails = null;
        // 从redis中获取订单数据
        String ordersJson = (String) redisTemplate.opsForHash().get(key, "order_" + pageNum);
        // 获取指定key中第一个指定的元素
        String firstPageJson = (String) redisTemplate.opsForHash().get(key, "order_1");
        // 如果redis中没有数据，则从数据库中获取
        if (ordersJson == null && firstPageJson == null) {
            updateRedisOrders(userId);
        } else if (ordersJson == null && firstPageJson != null) {
            // redis有指定数据，但hashKey（页码）不存在
            return null;
        }
        // redis有指定数据，且HashKey（页码）存在
        ordersJson = (String) redisTemplate.opsForHash().get(key, "order_" + pageNum);
        orderDetails = JSON.parseArray(ordersJson).toJavaList(OrderDetail.class);
        // 根据订单详情的skuId获取sku
        return orderDetails;
    }

    @Override
    public long getUserIdByOrderId(long orderId) {
        return orderMapper.getUserIdByOrderId(orderId);
    }

    // 更新redis中的订单数据
    @LogExecutionTime
    public void updateRedisOrders(long userId) {
        // key
        String key = "user_" + userId;
        // 从数据库中获取订单数据
        List<OrderDetail> orderDetails = orderMapper.getOrders((int) userId);
        // 根据订单详情的skuId获取sku
        for (OrderDetail orderDetail : orderDetails) {
            Query query = new Query(Criteria.where("skuId").is(orderDetail.getSkuId()));
            List<Skus> skus = mongoTemplate.find(query, Skus.class);
            orderDetail.setSku(skus.getFirst());
        }
        // 将完整订单数据存储到redis中
        int hashKey = 1;
        for (int i = 0; i < orderDetails.size(); i += 12) {
            int end = i + 12;
            if (end > orderDetails.size()) end = orderDetails.size();
            String subListJson = JSON.toJSONString(orderDetails.subList(i, end), JSONWriter.Feature.WriteBigDecimalAsPlain);
            redisTemplate.opsForHash().putAll(key, Collections.singletonMap("order_" + hashKey, subListJson));
            // 为刚存入的商品列表设置过期时间 10分钟, 防止缓存穿透
            hashKey++;
        }
        redisTemplate.expire(key, 12, TimeUnit.HOURS);
    }

}
